#if !defined(STD_OPTIONS_H)
#define STD_OPTIONS_H
// 
//  Add options as necessary to
// 
//  	optional
//  	config_file
//  	hidden
//  	mandatory

//
//  Some options are already defined in default_optional and these will be displayed
//  	in help messages AFTER any user-defined ones in optional.

//  You can also replace (discard) all defined options in optional or config_file by
//  creating a new set entirely:
// 
//  For example:
// 
//		#include "std_options.h"
//		#include <boost/program_options/options_description.hpp>
//		#include <boost/program_options/value_semantic.hpp>
//		#include <boost/shared_ptr.hpp>
//		#include <boost/foreach.hpp>
//		
//		namespace po = boost::program_options;
// 
//  	t_std_options opt("--summary_file FILE [options]", "1.0a");
// 
//  	std::string file_name_summary;                                                                                                         
//		boost::scoped_ptr<boost::program_options::options_description> new_default_optional(new boost::program_options::options_description());
//		BOOST_FOREACH(const boost::shared_ptr< po::option_description >& desc, opt.default_optional->options())                                
//			if (desc->long_name() == "summary_file")                                                                                           
//				new_default_optional->add_options()("summary_file,S",   po::value<std::string>(&file_name_summary)->required()                 
//											//->set_name("FILE"),                                                                              
//											,"Write summary to 2 FILE.");                                                                      
//			else                                                                                                                               
//				new_default_optional->add(desc);                                                                                               
//																																			   
//		new_default_optional.swap(opt.default_optional);                                                                                       
//
// 
//  We use scoped_ptr precisely so that defined options can be swapped out
//		(boost::options_description are fixed
// 
//
//  Examples:
// 
//   mandatory.add_options()
//   	("optimization", po::value<int>(&opt)	->default_value(10) ->required(), 
//  															"optimization level")
//		("include_path,I", 
//			po::value< vector<string> >()		->composing(), 	"include path")
//  	("input_file,i",				po::value(&input_file_name)->set_name("FILE")
//  												->default_value("STDIN"), 
//  															"Input file")
//	positional_options.add("include_path", -1);
//
//
//  optional.add_options() ("input_file,i",
//  							po::value<string>(&args.file_input)
//										->set_name("FILE")
//										->default_value("STDIN"),
//									"File containing cDNA sequence.")
//	("output_file,o",			po::value<string>(&args.file_output)
//										->set_name("FILE")
//										->default_value("STDOUT"),
//									"File which will contain translations in all 6 frames.");




#include <boost/scoped_ptr.hpp>
#include <boost/iostreams/filtering_stream.hpp>
#include <boost/iostreams/device/file.hpp>
#include <boost/iostreams/tee.hpp>
#include <boost/iostreams/stream.hpp>
#include <boost/program_options.hpp>
#include <boost/program_options/options_description.hpp>
#include <boost/any.hpp>
#include <iostream>


void parse_indices(const std::string& search_str, std::vector<unsigned>& indices);

class t_std_options
{
	// typedefs
	typedef boost::iostreams::tee_device<boost::iostreams::filtering_ostream, boost::iostreams::filtering_ostream> filtered_tee_device;
	typedef boost::iostreams::stream<filtered_tee_device> filtered_tee_stream;
	typedef boost::scoped_ptr<filtered_tee_stream>			sp_filtered_tee_stream;
	typedef boost::scoped_ptr<filtered_tee_device>			sp_filtered_tee_device;

private:
	unsigned 				max_line_length;
	bool					using_summary_file;
	bool					using_log_file;
	std::string				file_name_log;
	std::string 			file_name_summary;
	std::string				program_name;
	std::string				program_file_name;
	std::string				exe_description;
    std::string				program_usage;
	std::string				version_string;

	std::ostream&			open_summary_file();
	std::ostream&			open_log_file();
	std::ostream&			open_both();

	boost::iostreams::filtering_ostream	strm_summary;
	boost::iostreams::filtering_ostream	strm_log;
	boost::iostreams::filtering_ostream	strm_verbose;
	boost::iostreams::filtering_ostream	strm_null;
	sp_filtered_tee_device				p_device_both;
	sp_filtered_tee_stream				p_strm_both;


	boost::program_options::options_description cmdline_display_args;

	unsigned							verbosity;
public:
	t_std_options(const std::string& exe_description_ = "", const std::string& program_usage_ = "", const std::string& version_string_ = "", bool use_summary_file = false, unsigned max_line_length_ = 80);
	~t_std_options(){}

	void print_usage(std::ostream& os);
	void print_version(std::ostream& os);

	// Default command line options go after the user specified ones.
	// 		--version, --help, --configuration_file,
	//  	--skip_parameter_logging, --log_file
	// Included as scoped_ptr so can be replaced en masse. Not normally used
	boost::scoped_ptr<boost::program_options::options_description> default_optional  ;

	// Default options for the config file
	//  	--skip_parameter_logging, --log_file
	// Included as scoped_ptr so can be replaced en masse. Not normally used
	boost::scoped_ptr<boost::program_options::options_description> default_config_file	;

	// Options which are truly optional
	boost::program_options::options_description optional   	;
	// Options which will be parsed from the config file
	boost::program_options::options_description config_file	;
	// Options which are hidden
	boost::program_options::options_description hidden		;
	// Options which are mandatory
	boost::program_options::options_description mandatory	;
	// Options which are positional
	boost::program_options::positional_options_description positional_options;
	

	// logging
	std::ostream&		verbose(unsigned i = 1)	{return (verbosity >= i) 	? strm_verbose	: strm_null;			}
	std::ostream&		log() 					{return using_log_file 		? strm_log 		: open_log_file();		}
	std::ostream&		summary() 				{return using_summary_file 	? strm_summary 	: open_summary_file();	}
	std::ostream&		both()					{return p_strm_both			? *p_strm_both 	: open_both();			}
	std::ostream&		both(unsigned i)		{return (verbosity >= i) 	? both()		: log();				}
	unsigned			get_verbosity() const	{return verbosity;													}


	bool process_command_line(int argc, char* argv[]);
};




//________________________________________________________________________________________

//________________________________________________________________________________________
struct t_option_level {
public:
	unsigned n;
	t_option_level():n(0){}
	t_option_level& inc(){++n; return *this;}
};

template <typename U>
inline t_option_level* option_level(U* u)
{return reinterpret_cast<t_option_level*>(u);}


#endif 
